package cn.xinfei.xdecision.engine.api;

import cn.xinfei.xdecision.common.model.datax.consts.CommonConstant;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.JsonUtil;
import cn.xinfei.xdecision.common.utils.util.JsonUtils;
import cn.xinfei.xdecision.constant.EngineResultEnum;
import cn.xinfei.xdecision.constant.GlobalConstant;
import cn.xinfei.xdecision.engine.pre.PreProcess;
import cn.xinfei.xdecision.engine.runner.api.AbstractDecision;
import cn.xinfei.xdecision.engine.runner.api.DecisionDataApi;
import cn.xinfei.xdecision.engine.runner.api.DecisionReentryApi;
import cn.xinfei.xdecision.engine.runner.api.DecisionReentryApi2;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.metadata.PrometheusTagsConstants;
import cn.xinfei.xdecision.engine.runner.service.DecisionParamCheckServer;
import cn.xinfei.xdecision.engine.runner.vo.EngineApiRequest;
import cn.xinfei.xdecision.model.*;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("engine")
public class XDecisionApi {

    @Autowired
    @Qualifier("decisionDecisionApi")
    private AbstractDecision<EngineApiRequest, DecisionApiResponse> decisionDecisionApi;

    @Autowired
    @Qualifier("decisionApplyApi")
    private AbstractDecision<EngineApiRequest, DecisionApiResponse> decisionApplyApi;


    @Autowired
    private DecisionReentryApi decisionReentryApi;

    @Autowired
    private DecisionReentryApi2 decisionReentryApi2;

    @Autowired
    private DecisionDataApi decisionDataApi;

    @Autowired
    private MeterRegistry meterRegistry;

    @Autowired
    private DecisionParamCheckServer decisionParamCheckServer;


    /**
     * 决策申请接口
     *
     * @param apiRequest
     * @return
     */
    @PreProcess
    @RequestMapping(value = "/apply", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @ApiOperation(value = "决策申请接口")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", allowMultiple = true, name = "requestId", value = "requestId"),
            @ApiImplicitParam(paramType = "header", dataType = "String", allowMultiple = true, name = "tpCode", value = "tpCode")
    })
    public DecisionApiResponse apply(@RequestBody DecisionApiRequest apiRequest) {
        log.info("applyRequest:{}", JsonUtil.toJson(apiRequest));
        long start = System.currentTimeMillis();
        DecisionApiResponse decisionApiResponse = new DecisionApiResponse();
        Transaction transaction = Cat.newTransaction("xengine", "apply");
        try {

            String bizId = PipelineContextHolder.getRequestId();
            String engineCode = apiRequest.getEngineCode();

            String decisionId = PipelineContextHolder.getDecisionId();

            EngineApiRequest engineApiRequest = new EngineApiRequest();
            engineApiRequest.setEngineCode(engineCode);
            engineApiRequest.setIn(apiRequest.getFields());
            engineApiRequest.setRequestId(bizId);
            engineApiRequest.setDecisionId(decisionId);

            //检查引擎信息
            MDC.put(GlobalConstant.NID, engineCode);
            //1. 检查引擎信息
            EngineResultEnum engineResultEnum = decisionParamCheckServer.checkEngine(engineApiRequest);
            if (engineResultEnum != EngineResultEnum.PROCESSING) {
                decisionApiResponse.setEngineResult(engineResultEnum);
                return decisionApiResponse;
            }

            //2.验权
            EngineResultEnum engineResultEnum2 = decisionParamCheckServer.checkAuth(apiRequest);
            if (engineResultEnum2 != EngineResultEnum.PROCESSING) {
                decisionApiResponse.setEngineResult(engineResultEnum2);
                return decisionApiResponse;
            }

            //3.检查入参
            EngineResultEnum engineResultEnum3 = decisionParamCheckServer.checkParam(engineApiRequest);
            if (engineResultEnum3 != EngineResultEnum.PROCESSING) {
                decisionApiResponse.setEngineResult(engineResultEnum3);
                return decisionApiResponse;
            }

            PipelineContextHolder.setSync("ASYNC");

            //2.调用决策
            decisionApiResponse = decisionApplyApi.execute(engineApiRequest);

            transaction.setStatus(Transaction.SUCCESS);
        } catch (Exception e) {
            log.error("apply接口异常，error={},apiRequest={}", e.getMessage(), JsonUtil.toJson(apiRequest), e);
            decisionApiResponse.setEngineResult(EngineResultEnum.FAIL);
            Cat.logError(e); // 将异常上报到cat上
            transaction.setStatus(e);
        } finally {
            //3.处理返回
            long end = System.currentTimeMillis();
            decisionApiResponse.setRespond(end - start);
            log.info("applyDuration:{}", decisionApiResponse.getRespond());
            transaction.setDurationInMillis(decisionApiResponse.getRespond());
            transaction.complete();
            meterRegistry.counter("xengine_apply_total", PrometheusTagsConstants.XENGINE_API_TOTAL_TAGS.and("code", decisionApiResponse.getCode())).increment();
            Metrics.gauge("xengine_apply_respond", PrometheusTagsConstants.XENGINE_API_RESPOND_TAGS, decisionApiResponse.getRespond());
            MDC.clear();
        }
        return decisionApiResponse;
    }


    /**
     * 决策接口
     *
     * @param apiRequest
     * @return
     */
    @PreProcess
    @ApiOperation(value = "决策接口")
    @RequestMapping(value = "/decision", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", allowMultiple = true, name = "requestId", value = "requestId"),
            @ApiImplicitParam(paramType = "header", dataType = "String", allowMultiple = true, name = "tpCode", value = "tpCode")
    })
    public DecisionApiResponse decision(@RequestBody DecisionApiRequest apiRequest) {
        log.info("decisionRequest:{}", JsonUtil.toJson(apiRequest));
        long start = System.currentTimeMillis();
        DecisionApiResponse decisionApiResponse = new DecisionApiResponse();
        Transaction transaction = Cat.newTransaction("xengine", "decision");
        try {

            String bizId = PipelineContextHolder.getRequestId();
            String scene = PipelineContextHolder.getScene();
            String engineCode = apiRequest.getEngineCode();


            String decisionId = PipelineContextHolder.getDecisionId();


            EngineApiRequest engineApiRequest = new EngineApiRequest();
            engineApiRequest.setEngineCode(engineCode);
            engineApiRequest.setIn(apiRequest.getFields());
            engineApiRequest.setRequestId(bizId);
            engineApiRequest.setDecisionId(decisionId);


            //1. 检查引擎信息
            MDC.put(GlobalConstant.NID, engineCode);
            EngineResultEnum engineResultEnum = decisionParamCheckServer.checkEngine(engineApiRequest);
            if (engineResultEnum != EngineResultEnum.PROCESSING) {
                decisionApiResponse.setEngineResult(engineResultEnum);
                log.error("决策流检测无效，request={}", JsonUtil.toJson(engineApiRequest));
                return decisionApiResponse;
            }

            //2.验权
            EngineResultEnum engineResultEnum2 = decisionParamCheckServer.checkAuth(apiRequest);
            if (engineResultEnum2 != EngineResultEnum.PROCESSING) {
                decisionApiResponse.setEngineResult(engineResultEnum2);
                return decisionApiResponse;
            }

            //3.检查入参
            EngineResultEnum engineResultEnum3 = decisionParamCheckServer.checkParam(engineApiRequest);
            if (engineResultEnum3 != EngineResultEnum.PROCESSING) {
                decisionApiResponse.setEngineResult(engineResultEnum3);
                log.error("决策流入参检测无效，request={}", JsonUtil.toJson(engineApiRequest));
                return decisionApiResponse;
            }

            PipelineContextHolder.setSync("SYNC");

            //2.调用决策
            PipelineContextHolder.setEngineResult(EngineResultEnum.PROCESSING);
            decisionApiResponse = decisionDecisionApi.execute(engineApiRequest);

            //3.处理返回
            String result = decisionApiResponse.getResult();
            if (!"REJECT".equals(result) && !CommonConstant.STATUS_PASS.equals(result)) {
                //只是在此做一下简单的result处理，不改变底层存储结果。上游调用时state=fail时就抛异常
                meterRegistry.counter("xengine_decision_result_total").increment();
            }

            transaction.setStatus(Transaction.SUCCESS);
        } catch (Exception e) {
            log.error("决策接口异常，error={},apiRequest={}", e.getMessage(), JsonUtils.toJsonString(apiRequest), e);
            decisionApiResponse.setEngineResult(EngineResultEnum.FAIL);
            decisionApiResponse.setMsg("申请受理异常，请重试");
            Cat.logError(e); // 将异常上报到cat上
            transaction.setStatus(e);
        } finally {
            long end = System.currentTimeMillis();
            decisionApiResponse.setRespond(end - start);
            log.info("decisionDuration:{}", decisionApiResponse.getRespond());
            transaction.setDurationInMillis(decisionApiResponse.getRespond());
            transaction.complete();
            meterRegistry.counter("xengine_decision_total", PrometheusTagsConstants.XENGINE_API_TOTAL_TAGS.and("code", decisionApiResponse.getCode())).increment();
            Metrics.gauge("xengine_decision_respond", PrometheusTagsConstants.XENGINE_API_RESPOND_TAGS, decisionApiResponse.getRespond());
        }
        return decisionApiResponse;
    }


    /**
     * 重入
     *
     * @param apiRequest
     * @return
     */
    @ApiOperation(value = "决策重入接口")
    @RequestMapping(value = "/reentry", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public DecisionReentryResponse reentry(@RequestBody DecisionReentryApiRequest apiRequest) {
        DecisionReentryResponse decisionApiResponse = new DecisionReentryResponse();
        Transaction transaction = Cat.newTransaction("xengine", "reentry");
        long start = System.currentTimeMillis();
        log.info("reentryRequest:{}", apiRequest);
        try {
            XDecisionContext.destroy();
            decisionApiResponse = decisionReentryApi.reentry(apiRequest);
            log.info("reentryResponse={}", decisionApiResponse);
            transaction.setStatus(Transaction.SUCCESS);
        } catch (Exception e) {
            log.error("重入接口异常，error={},reentryRequest={}", e.getMessage(), JsonUtils.toJsonString(apiRequest), e);
            decisionApiResponse.setMsg("重入接口异常:" + e.getMessage());
            Cat.logError(e); // 将异常上报到cat上
            transaction.setStatus(e);
        } finally {
            long end = System.currentTimeMillis();
            long mills = end - start;
            transaction.setDurationInMillis(mills);
            decisionApiResponse.setRespond(mills);
            transaction.complete();
            log.info("riskReentryApi reentryJobDuration:{}", decisionApiResponse.getRespond());
            MDC.clear();
        }

        return decisionApiResponse;
    }

    @ApiOperation(value = "决策重入接口-支持重复决策")
    @RequestMapping(value = "/reentry2", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public DecisionReentryResponse reentry2(@RequestBody DecisionReentryApiRequest apiRequest) {
        DecisionReentryResponse decisionApiResponse = new DecisionReentryResponse();
        Transaction transaction = Cat.newTransaction("xengine", "reentry");
        long start = System.currentTimeMillis();
        log.info("reentry2Request:{}", apiRequest);
        try {
            XDecisionContext.destroy();
            decisionApiResponse = decisionReentryApi2.reentry(apiRequest);
            log.info("reentry2Response={}", decisionApiResponse);
            transaction.setStatus(Transaction.SUCCESS);
        } catch (Exception e) {
            log.error("重入接口异常，error={},reentry2Request={}", e.getMessage(), JsonUtils.toJsonString(apiRequest), e);
            decisionApiResponse.setMsg("重入接口异常:" + e.getMessage());
            Cat.logError(e); // 将异常上报到cat上
            transaction.setStatus(e);
        } finally {
            long end = System.currentTimeMillis();
            long mills = end - start;
            transaction.setDurationInMillis(mills);
            decisionApiResponse.setRespond(mills);
            transaction.complete();
            log.info("decisionReentryApi2 reentryJobDuration:{}", decisionApiResponse.getRespond());
            MDC.clear();
        }

        return decisionApiResponse;
    }


    /**
     * 查询决策结果接口
     *
     * @param apiRequest
     * @return
     */
    @ApiOperation(value = "决策结果查询接口")
    @RequestMapping(value = "/query", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public DecisionApiResponse query(@RequestBody DecisionDataApiRequest apiRequest) throws Exception {
        Transaction transaction = Cat.newTransaction("xengine", "query");
        DecisionApiResponse responses = null;
        long start = System.currentTimeMillis();
        try {
            log.info("queryRequest={}", JsonUtils.toJsonString(apiRequest));

            if (StringUtils.isEmpty(apiRequest.getDecisionId()) || StringUtils.isEmpty(apiRequest.getRequestId())) {
                throw new ApiException(ErrorCodeEnum.REQUEST_ID_AND_DECISION_ID_CAN_NOT_NULL);
            }
            responses = decisionDataApi.query(apiRequest);
            log.info("queryResponse={}", JsonUtils.toJsonString(responses));

            transaction.setStatus(Transaction.SUCCESS);
            Metrics.counter("xengine_query_total", PrometheusTagsConstants.XENGINE_API_TOTAL_TAGS.and("code", "SUCCESS")).increment();
        } catch (Exception e) {
            log.error("查询决策结果接口异常，error={},queryRequest={}", e.getMessage(), JsonUtils.toJsonString(apiRequest), e);
            Cat.logError(e); // 将异常上报到cat上
            transaction.setStatus(e);
            responses.setEngineResult(EngineResultEnum.FAIL);
            Metrics.counter("xengine_query_total", PrometheusTagsConstants.XENGINE_API_TOTAL_TAGS.and("code", "FAIL")).increment();
            throw e;
        } finally {
            long end = System.currentTimeMillis();
            long respond = end - start;
            log.info("queryDuration:{}", respond);
            transaction.setDurationInMillis(respond);
            responses.setRespond(respond);
            transaction.complete();
            MDC.clear();
        }
        return responses;
    }
}
